#ifndef __LLIST_H__
#define __LLIST_H__

#include <assert.h>
#include <exception>

#include "ListIter.h"

class ListException:public std::exception
{
    public:
        const char* what() const throw();
};

/******     class declerations      *****/
template <class T>
class LList;

template <class T>
class ConstIterator;

template <class T>
class Iterator;

/****   Decleration of friends non member functions    ***/
template <class T>
inline bool operator==(const ConstIterator<T>&, const ConstIterator<T>&);
/******   Declerations of non member functions    ********/
template <class T>
inline bool operator==(const Iterator<T>& _it1, const Iterator<T>& _it2);

template <class T>
inline bool operator==(const ConstIterator<T>& _cit1, const Iterator<T>& _it2);

template <class T>
inline bool operator==(const Iterator<T>& _it1, const ConstIterator<T>& _cit2);

//      operator!=
template <class T>
inline bool operator!=(const ConstIterator<T>& _cit1, const ConstIterator<T>& _cit2);

template <class T>
inline bool operator!=(const Iterator<T>& _it1, const Iterator<T>& _it2);

template <class T>
inline bool operator!=(const ConstIterator<T>& _cit1, const Iterator<T>& _it2);

template <class T>
inline bool operator!=(const Iterator<T>& _it1, const ConstIterator<T>& _cit2);
/*************************************************************/

template <class T>
class Iterator
{
 public:
    //  use default DTOR
    //  use default copy CTOR
    //  use default Assigment operator
    inline Iterator& operator++();
    inline Iterator& operator--();
    inline T*  Get() const;                            //  Get also from const Iterator

 private:
    /**     Friends                     **/
    friend Iterator LList<T>::Begin();  //  friend functions for building from private CTOR Iterator
    friend Iterator LList<T>::End();
    friend ConstIterator<T> LList<T>::Begin() const;
    friend ConstIterator<T> LList<T>::End() const;


    friend bool operator==<T>(const ConstIterator<T>&, const ConstIterator<T>&);    //  friend logic operator for comparing iterators
    friend Iterator<T> LList<T>::Insert(ConstIterator<T> , T*);
    friend Iterator<T> LList<T>::Remove(ConstIterator<T>);

    /**     Data members                **/
    ListIter m_iter;
#ifdef _DEBUG
    List*   m_list;
#endif  //  #ifdef _DEBUG

    /**     Private member functions    **/
    inline ListIter GetListIter() const;       //  Get to private data member
    //  private CTOR from ListIter. can only initalize in Begin() or End() methods of LList.
    Iterator(ListIter
#ifdef _DEBUG           //  in debug mode also another parameter for init the value of poined list
    , List*
#endif  //  #ifdef _DEBUG
                    );
};


//  private inheritance from class Iterator
template <class T>
class ConstIterator:private Iterator<T>
{
 public:
    //  use default DTOR
    //  use default CTOR
    //  use default Assigment operator
    ConstIterator(const Iterator<T>&);              //  CTOR from Iterator for implicit conversion

//  from private to public the ++ function and -- function
    using Iterator<T>::operator++;
    using Iterator<T>::operator--;

    inline const T* Get() const;

 private:
    friend bool operator==<T>(const ConstIterator<T>&, const ConstIterator<T>&);
    friend ConstIterator LList<T>::Begin() const;
    friend ConstIterator LList<T>::End() const;
    friend Iterator<T> LList<T>::Insert(ConstIterator<T> , T*);
    friend Iterator<T> LList<T>::Remove(ConstIterator<T>);
};


template <class T>
class LList
{
  public:
    virtual ~LList();                   //  DTOR
    LList();                            //  CTOR
    LList(const LList& );               //  copy CTOR
    LList& operator=(const LList& );    //  Assigment operator

    typedef ::Iterator<T> Iterator;
    typedef ::ConstIterator<T> ConstIterator;

    inline ConstIterator Begin() const;        //  for const list return ConstIterator for not changing data
    inline ConstIterator End()   const;

    inline Iterator    Begin();
    inline Iterator    End();

    inline Iterator    Insert(ConstIterator , T*);
    inline Iterator    Remove(ConstIterator);

    inline bool        IsEmpty() const;    //  relevant also for const List

  private:
    /**     Data members                **/
    List* m_list;
    /**     Private member functions    **/
    inline void CreateFrom(const LList& _list);
};

/**********************************************************************************************************************************************/
/*******************************************      Implementation of Template Functions        *************************************************/
/**********************************************************************************************************************************************/

template <class T>
inline LList<T>::~LList()                           //  DTOR
{
    ListDelete(m_list);
    m_list = 0;
}


template <class T>
inline LList<T>::LList()                            //  CTOR
{
    m_list = ListNew();
    if(!m_list)
    {
        ListException e;
        throw(e);
    }
    assert(m_list);
}


template <class T>
inline LList<T>::LList(const LList& _list)               //  copy CTOR
{
      CreateFrom(_list);
}

template <class T>
inline LList<T>& LList<T>::operator=(const LList& _list)    //  Assigment operator
{
    if(&_list != this)
    {
        ListDelete(m_list);
        CreateFrom(_list);
    }

    return *this;
}


template <class T>
inline void LList<T>::CreateFrom(const LList& _list)
{
    m_list = ListNew();
    if(!m_list)
    {
        ListException e;
        throw(e);
    }
    assert(m_list);
    ConstIterator cit(_list.Begin());

    while(cit != _list.End())
    {
        Insert(End(), const_cast<T*>(cit.Get()));       //  For inserting T* to list and not const T*
        ++cit;
    }
}


template <class T>
inline Iterator<T>& Iterator<T>::operator++()
{
#ifdef _DEBUG
    assert(m_iter != ListEnd(m_list));
#endif  //  #ifdef _DEBUG
    m_iter = ListNext(m_iter);
    return *this;
}

template <class T>
inline Iterator<T>& Iterator<T>::operator--()
{
    assert(m_iter != ListBegin(m_list));

    m_iter = ListPrev(m_iter);
    return *this;
}

template <class T>
inline T*  Iterator<T>::Get() const                            //  Get also from const Iterator
{
    assert(m_iter != ListEnd(m_list));
    assert(m_iter != (void*)0xdeadbeef);
    assert(m_list != (void*)0xdeadbeef);

    return static_cast<T*>(ListGetData(m_iter));
}


template <class T>
inline const T*  ConstIterator<T>::Get() const                            //  Get also from const Iterator
{
    return Iterator<T>::Get();
}


template <class T>
inline typename LList<T>::ConstIterator LList<T>::Begin() const
{
    return Iterator(ListBegin(m_list)
#ifdef _DEBUG
    , m_list     //  init the current list
#endif  //  #ifdef _DEBUG
                                    );
}


template <class T>
inline typename LList<T>::ConstIterator LList<T>::End() const
{
    return Iterator(ListEnd(m_list)
#ifdef _DEBUG
    , m_list     //  init the current list
#endif  //  #ifdef _DEBUG
                                    );
}


//  return reinterpret_cast<Iterator<T>&> ((const_cast<const LList<T>* >(this))->Begin());

template <class T>
inline typename LList<T>::Iterator  LList<T>::Begin()
{
    return Iterator(ListBegin(m_list)
#ifdef _DEBUG
    , m_list     //  init the current list
#endif  //  #ifdef _DEBUG
                                    );
}

template <class T>
inline typename LList<T>::Iterator  LList<T>::End()
{
    return Iterator(ListEnd(m_list)
#ifdef _DEBUG
    , m_list     //  init the current list
#endif  //  #ifdef _DEBUG
                                    );
}

template <class T>
inline typename LList<T>::Iterator  LList<T>::Insert(ConstIterator _it, T* _item)
{
    assert(_it.m_list == m_list );
    assert(_it.m_iter != (void*)0xdeadbeef);

    return Iterator(ListInsert(_it.GetListIter() , _item)
#ifdef _DEBUG
    , m_list     //  init the current list
#endif  //  #ifdef _DEBUG
                                                    );
}

template <class T>
inline typename LList<T>::Iterator  LList<T>::Remove(ConstIterator _it)
{
    assert(_it.m_list == m_list );

    Iterator it(ListRemove(_it.GetListIter())
#ifdef _DEBUG
    , m_list     //  init the current list
#endif  //  #ifdef _DEBUG
                                                    );
    it.m_iter = (ListIter)0xdeadbeef;
    it.m_list = (List*)0xdeadbeef;

    return it;
}


template <class T>
inline ConstIterator<T>::ConstIterator(const Iterator<T>& _it) :Iterator<T>(_it)
{   }

template <class T>
inline Iterator<T>::Iterator(ListIter _lit
#ifdef _DEBUG
, List* _list
#endif  //  #ifdef _DEBUG

)  :m_iter(_lit)

#ifdef _DEBUG
, m_list(_list)             //  init the current list in debug mode
#endif  //  #ifdef _DEBUG
{   }

template <class T>
inline bool LList<T>::IsEmpty() const
{
    return ListIsEmpty(m_list);
}


//  get function for private data member
template <class T>
inline ListIter Iterator<T>::GetListIter() const
{
    return m_iter;
}


//      operator==
template <class T>
inline bool operator==(const ConstIterator<T>& _it1, const ConstIterator<T>& _it2)
{
#ifdef _DEBUG
    assert(_it1.m_list == _it2.m_list);
#endif  //  #ifdef _DEBUG

    return ListIterIsEqual(_it1.GetListIter(), _it2.GetListIter());
}

template <class T>
inline bool operator==(const Iterator<T>& _it1, const Iterator<T>& _it2)
{
    return ConstIterator<T>(_it1) == ConstIterator<T>(_it2);
}

template <class T>
inline bool operator==(const ConstIterator<T>& _cit1, const Iterator<T>& _it2)
{
    return _cit1 == ConstIterator<T>(_it2);
}

template <class T>
inline bool operator==(const Iterator<T>& _it1, const ConstIterator<T>& _cit2)
{
    return ConstIterator<T>(_it1) == _cit2;
}


//      operator!=

template <class T>
inline bool operator!=(const ConstIterator<T>& _cit1, const ConstIterator<T>& _cit2)
{
    return !(_cit1 == _cit2);
}

template <class T>
inline bool operator!=(const Iterator<T>& _it1, const Iterator<T>& _it2)
{
    return ConstIterator<T>(_it1) != ConstIterator<T>(_it2);
}

template <class T>
inline bool operator!=(const ConstIterator<T>& _cit1, const Iterator<T>& _it2)
{
    return _cit1 != ConstIterator<T>(_it2);
}

template <class T>
inline bool operator!=(const Iterator<T>& _it1, const ConstIterator<T>& _cit2)
{
    return ConstIterator<T>(_it1) != _cit2;
}


#endif  //  __LLIST_H__
